/**
 *
 * @author garison
 * @author Marek
 */
import javax.swing.*;
import java.net.*;
import java.io.*;
import java.util.*;
import java.text.*;
import java.awt.*;
import java.awt.print.PageFormat;
import java.awt.print.Paper;
import java.awt.print.Printable;
import java.awt.print.PrinterException;
import java.awt.print.PrinterJob;
import java.awt.print.Paper;
import java.util.Date;
import java.util.Calendar;
import javax.swing.GroupLayout.Alignment;
import javax.swing.LayoutStyle.ComponentPlacement;
import java.awt.event.ActionListener;
import java.awt.event.ActionEvent;
import java.net.URL;


public class sundialGui extends javax.swing.JDialog {
    
    double latitude;
    double longitude;
    double day;
    double month;
    double year;
    double date;
    boolean DST;
    boolean validInput = true;
    String sDate;
    SundialFace   face;
    SundialGnomon gnomon;
	 private static final String GEO_CODE_SERVER = "http://maps.googleapis.com/maps/api/geocode/json?";

    
    // For the current date
	Calendar cal = Calendar.getInstance();
	SimpleDateFormat dateFormat = new SimpleDateFormat ("MM/dd/yyyy");
    
    
    /**
     * Creates new form NewJDialog
     */
    public sundialGui(java.awt.Frame parent, boolean modal) {
        super(parent, modal);
        initComponents();
    }

    /**
     * This method is called from within the constructor to initialize the form.
     * WARNING: Do NOT modify this code. The content of this method is always
     * regenerated by the Form Editor.
     */
    @SuppressWarnings("unchecked")
    public void initComponents() {
	
	    jTextField5 = new javax.swing.JTextField(); //latitude
        jTextField1 = new javax.swing.JTextField(); //longitude
        jTextField3 = new javax.swing.JTextField(); //month
        jTextField4 = new javax.swing.JTextField(); //day
	    jTextField2 = new javax.swing.JTextField(); //year
        jLabel1 = new javax.swing.JLabel();
        jLabel2 = new javax.swing.JLabel();
        jLabel3 = new javax.swing.JLabel();
        jButton1 = new javax.swing.JButton();
        jButton2 = new javax.swing.JButton();
        jLabel4 = new javax.swing.JLabel();
        
        // Split the date for default values in jTextFields
        sDate = dateFormat.format(cal.getTime());
        System.out.println(sDate);
        setDefaultCloseOperation(javax.swing.WindowConstants.DISPOSE_ON_CLOSE);

        //set default values
        jTextField1.setText("157.7964");

        jTextField2.setText(sDate.substring(sDate.lastIndexOf("/")+1));

        jTextField3.setText(sDate.substring(0, sDate.indexOf("/")));

        jTextField4.setText(sDate.substring(sDate.indexOf("/")+1, sDate.lastIndexOf("/")));

        jLabel1.setText("Latitude (0.0000)");

        jLabel2.setText("Longitude (0.0000)");

        jLabel3.setText("Date (MM/DD/YYYY)");

        //button to draw 2nd window
        jButton1.setText("Draw Sundial");
	    jButton1.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                try {
					jButton1ActionPerformed(evt);
				} catch (Exception e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
            }
        });

        //button toggles dst
         jButton2.setText("DST");
        jButton2.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                jButton2ActionPerformed(evt);
            }
        });

        jLabel4.setText("Sundial Generator");

        jTextField5.setText("21.3114");
        
        JLabel lblNewLabel = new JLabel("Lookup Coordinates");
        
        txtEnterAddress = new JTextField();
        txtEnterAddress.setText("Enter Address");
        txtEnterAddress.setColumns(10);
        
        JButton btnSearch = new JButton("Search");
        btnSearch.addActionListener(new ActionListener() {
        	public void actionPerformed(ActionEvent arg0) {
			btnSearchActionPerformed(arg0);
        	}
        });
        
        txtOff = new JTextField();
        txtOff.setText("off");
        txtOff.setColumns(10);

        javax.swing.GroupLayout layout = new javax.swing.GroupLayout(getContentPane());
        layout.setHorizontalGroup(
        	layout.createParallelGroup(Alignment.TRAILING)
        		.addGroup(layout.createSequentialGroup()
        			.addGap(121)
        			.addGroup(layout.createParallelGroup(Alignment.LEADING)
        				.addComponent(lblNewLabel)
        				.addComponent(jLabel3)
        				.addComponent(jLabel2)
        				.addComponent(jLabel1))
        			.addGap(12)
        			.addGroup(layout.createParallelGroup(Alignment.LEADING)
        				.addComponent(txtEnterAddress, GroupLayout.DEFAULT_SIZE, 257, Short.MAX_VALUE)
        				.addGroup(layout.createSequentialGroup()
        					.addComponent(jTextField3, GroupLayout.PREFERRED_SIZE, 31, GroupLayout.PREFERRED_SIZE)
        					.addGap(4)
        					.addComponent(jTextField4, GroupLayout.PREFERRED_SIZE, 32, GroupLayout.PREFERRED_SIZE)
        					.addPreferredGap(ComponentPlacement.RELATED)
        					.addComponent(jTextField2, GroupLayout.PREFERRED_SIZE, 49, GroupLayout.PREFERRED_SIZE)
        					.addPreferredGap(ComponentPlacement.UNRELATED)
        					.addComponent(jButton2)
        					.addPreferredGap(ComponentPlacement.UNRELATED)
        					.addComponent(txtOff, GroupLayout.DEFAULT_SIZE, 36, Short.MAX_VALUE))
        				.addComponent(jTextField5, GroupLayout.DEFAULT_SIZE, 257, Short.MAX_VALUE)
        				.addComponent(jTextField1, GroupLayout.DEFAULT_SIZE, 257, Short.MAX_VALUE))
        			.addPreferredGap(ComponentPlacement.UNRELATED)
        			.addComponent(btnSearch)
        			.addGap(164))
        		.addGroup(layout.createSequentialGroup()
        			.addContainerGap(338, Short.MAX_VALUE)
        			.addComponent(jButton1)
        			.addGap(316))
        		.addGroup(layout.createSequentialGroup()
        			.addGap(341)
        			.addComponent(jLabel4, GroupLayout.DEFAULT_SIZE, 127, Short.MAX_VALUE)
        			.addGap(312))
        );
        layout.setVerticalGroup(
        	layout.createParallelGroup(Alignment.TRAILING)
        		.addGroup(layout.createSequentialGroup()
        			.addGap(22)
        			.addComponent(jLabel4, GroupLayout.DEFAULT_SIZE, 36, Short.MAX_VALUE)
        			.addGap(27)
        			.addGroup(layout.createParallelGroup(Alignment.BASELINE)
        				.addComponent(jLabel1)
        				.addComponent(jTextField5, GroupLayout.PREFERRED_SIZE, 26, GroupLayout.PREFERRED_SIZE))
        			.addPreferredGap(ComponentPlacement.UNRELATED)
        			.addGroup(layout.createParallelGroup(Alignment.BASELINE)
        				.addComponent(jTextField1, GroupLayout.PREFERRED_SIZE, 27, GroupLayout.PREFERRED_SIZE)
        				.addComponent(jLabel2))
        			.addGap(10)
        			.addGroup(layout.createParallelGroup(Alignment.BASELINE)
        				.addComponent(jTextField4, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE)
        				.addComponent(jTextField2, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE)
        				.addComponent(jTextField3, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE)
        				.addComponent(jLabel3)
        				.addComponent(jButton2)
        				.addComponent(txtOff, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE))
        			.addPreferredGap(ComponentPlacement.UNRELATED)
        			.addGroup(layout.createParallelGroup(Alignment.BASELINE)
        				.addComponent(txtEnterAddress, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE)
        				.addComponent(lblNewLabel)
        				.addComponent(btnSearch))
        			.addGap(18)
        			.addComponent(jButton1)
        			.addGap(35))
        );
        getContentPane().setLayout(layout);

        pack();
    }

    /***********************************************************
    * jButton2ActionPerformed
    * 
    * sets daylightsavings time to true or false
    *
    * Parameters: Actionevent evt
    *
    * Returns: Nothing
    ***********************************************************/
    private void jButton2ActionPerformed(java.awt.event.ActionEvent evt) {

    if (DST == true)
    {
        DST = false;
        txtOff.setText("on");
    }

    else if (DST == false)
    {
        DST = true;
        txtOff.setText("off");
    }

    }
	 
	 
	 private void btnSearchActionPerformed(java.awt.event.ActionEvent evt) {
		
		try{
		String code = txtEnterAddress.getText();
		
      String response = getLocation(code);

      String[] result = parseLocation(response);
		
		jTextField5.setText(result[0]);
		jTextField1.setText(result[1]);
		}catch(java.lang.RuntimeException ioe)
		{
		 txtEnterAddress.setText("No Internet Connection!");
		}

    }


    /***********************************************************
    * jButton1ActionPerformed
    * 
    * Does input validation checking leap year, days of month,
    * input other than doubles etc.
    *
    * Parameters: Actionevent evt
    *
    * Returns: Nothing
     * @throws Exception 
    ***********************************************************/
    private void jButton1ActionPerformed(java.awt.event.ActionEvent evt) throws Exception {

	try
    { 
      //set latitude
       if (Double.parseDouble(jTextField5.getText()) >= 10.0 && Double.parseDouble(jTextField5.getText()) <= 80.0)
       {
           latitude = Double.parseDouble(jTextField5.getText());
       }

       else
       {
         JOptionPane.showMessageDialog(null, "Latitude must be a double with a range of 10.0 to 80.0", "Error", JOptionPane.ERROR_MESSAGE);
         validInput = false;
       }

	}catch(NumberFormatException e){
        validInput = false;
        jTextField5.setText("10.0000");
	    JOptionPane.showMessageDialog(null, "Latitude must be a double with a range of 10.0 to 80.0", "Error", JOptionPane.ERROR_MESSAGE);

	}

	try
    {
       //set longitude
	   if (Double.parseDouble(jTextField1.getText()) >= -180.0 && Double.parseDouble(jTextField1.getText()) <= 180.0) 
	   {
	       longitude = Double.parseDouble(jTextField1.getText());
	   }

       else
       {
           JOptionPane.showMessageDialog(null, "Longitude must be a double with a range of -180.0 to 180.0", "Error", JOptionPane.ERROR_MESSAGE);
           validInput = false;
       } 

	}catch(NumberFormatException e){
        validInput = false;
        jTextField1.setText("10.0000");
	    JOptionPane.showMessageDialog(null, "Longitude must be a double!", "Error", JOptionPane.ERROR_MESSAGE);
	}

	try
    {
       //set month
	   month = Double.parseDouble(jTextField3.getText());

	   if (Double.parseDouble(jTextField3.getText()) >= 1.0 && Double.parseDouble(jTextField3.getText()) <= 12.0)
	   {
	       month = Double.parseDouble(jTextField3.getText());
           --month; //decrement month as calculation code starts months from 0 (to 11)
	   }

	   else 
	   {  
          validInput = false;
		  JOptionPane.showMessageDialog(null, "Valid months are 1-12", "Error", JOptionPane.ERROR_MESSAGE);
          jTextField3.setText("01");
		  month = 1.0;
	   }

	}catch(NumberFormatException e){
        validInput = false;
	    JOptionPane.showMessageDialog(null, "Valid months are 1-12", "Error", JOptionPane.ERROR_MESSAGE);
        jTextField3.setText("01");
	}

	try
    {
       //set year
	   year = Double.parseDouble(jTextField2.getText());	

	   if (Double.parseDouble(jTextField2.getText()) >= 1583 && Double.parseDouble(jTextField2.getText()) <= 9999)
	   {
	       year = Double.parseDouble(jTextField2.getText());
	   }

	   else
	   {
          validInput = false;
		  JOptionPane.showMessageDialog(null, "Valid years are 1583-9999", "Error", JOptionPane.ERROR_MESSAGE);
		  jTextField2.setText("2000");
		  year = 2000.0;	
	   }

	}catch(NumberFormatException e){
        validInput = false;
	    JOptionPane.showMessageDialog(null, "Valid years are 1583-9999", "Error", JOptionPane.ERROR_MESSAGE);
        jTextField2.setText("2000");
	}

	try
    {
        //set day
	    day = Double.parseDouble(jTextField4.getText());

        if (month == 0.0 || month == 2.0 || month == 4.0 || month == 6.0 || month == 7.0 || month == 9.0 || month == 11.0) 
	    {
		
 		     if (day >= 1.0 && day <= 31.0)	
		     {
			     day = Double.parseDouble(jTextField4.getText());
		     }

		     else
		     {   
			     JOptionPane.showMessageDialog(null, "Months 1, 3, 5, 7, 8, 10 and 12 only have 31 days!", "Error", JOptionPane.ERROR_MESSAGE);
		         jTextField4.setText("15");
		         day = 15.0;	
                 validInput = false;
		     }
;	
	    }
		
	    if (month == 3.0 || month == 5.0 || month == 9.0 || month == 10.0)
	    {
		      if (day >= 1.0 && day <= 30.0)
		      {
			     day = Double.parseDouble(jTextField4.getText()); 
		      }

		      else
		      {  
                 validInput = false;
			     JOptionPane.showMessageDialog(null, "Months 4, 6, 10 and 11 only have 30 days!", "Error", JOptionPane.ERROR_MESSAGE);
		         jTextField4.setText("15");
		         day = 15.0;
		      }
;	
	    }

	    if (month == 1)
	    {
                if (isLeapYear((int)year) == false)
		        {
	               if (day >= 1.0 && day <= 28.0)
		           {
			           day = Double.parseDouble(jTextField4.getText()); 
		           }

	               else
		           {   
                       validInput = false;
			           JOptionPane.showMessageDialog(null, "Month 2 cannot have more than 28 days (as inputted year is not a leap year)", "Error", JOptionPane.ERROR_MESSAGE);
		               jTextField4.setText("15");
		               day = 15.0;
		           }
;
	   	        }

		else if (isLeapYear((int)year) == true)
		{
		    if (day >= 1.0 && day <= 29.0)
		    {
			     day = Double.parseDouble(jTextField4.getText());
		    }

		    else
		    {    
                 validInput = false;
			     JOptionPane.showMessageDialog(null, "Month 2 cannot have more than 29 days (as inputted year is a leap year)", "Error", JOptionPane.ERROR_MESSAGE);
		         jTextField4.setText("15");
		         day = 15.0;
		    }
;
		    }
	    }
		
	
	}catch(NumberFormatException e){
        validInput = false;
	    JOptionPane.showMessageDialog(null, "Specified day must be a valid day of the month i.e. 1-31", "Error", JOptionPane.ERROR_MESSAGE);
        jTextField4.setText("15");
	}
	
    //Set calendar object to pass to Calculation code to apply EOT
	cal.set(Calendar.YEAR, (int)year);
	cal.set(Calendar.MONTH, (int)month);
	cal.set(Calendar.DAY_OF_MONTH, (int)day);

    //pass values to Calculation class
	sundialCalculation sc = new sundialCalculation(latitude, longitude, cal);
	
	
    if (validInput)
    {

	try{
        
	   //draw output windows
	   face    = new SundialFace(500, latitude, sc.getAdjustedHourAngles(), DST);
	   gnomon  = new SundialGnomon(500, latitude);	   
	
	}catch(Exception e)
    {
	  System.out.println("Out of bounds");
	}
	 
	  print(gnomon);
	  print(face);
    }
   
	validInput = true;
    /* tests with console
    System.out.println(latitude);
	System.out.println(longitude);
	System.out.println(day);
	System.out.println(month);
	System.out.println(year);
    */

    }

    /**
     * @param args the command line arguments
     */
    public static void main(String args[]) {
	    /* Set the Nimbus look and feel */
        //<editor-fold defaultstate="collapsed" desc=" Look and feel setting code (optional) ">
        /* If Nimbus (introduced in Java SE 6) is not available, stay with the default look and feel.
         * For details see http://download.oracle.com/javase/tutorial/uiswing/lookandfeel/plaf.html 
         */	 
		 
        try {
            for (javax.swing.UIManager.LookAndFeelInfo info : javax.swing.UIManager.getInstalledLookAndFeels()) {
                if ("Nimbus".equals(info.getName())) {
                    javax.swing.UIManager.setLookAndFeel(info.getClassName());
                    break;
                }
            }
        } catch (ClassNotFoundException ex) {
            java.util.logging.Logger.getLogger(sundialGui.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
        } catch (InstantiationException ex) {
            java.util.logging.Logger.getLogger(sundialGui.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
        } catch (IllegalAccessException ex) {
            java.util.logging.Logger.getLogger(sundialGui.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
        } catch (javax.swing.UnsupportedLookAndFeelException ex) {
            java.util.logging.Logger.getLogger(sundialGui.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
        }


        /* Create and display the dialog */
        java.awt.EventQueue.invokeLater(new Runnable() {
            public void run() {
                sundialGui dialog = new sundialGui(new javax.swing.JFrame(), false);
                dialog.addWindowListener(new java.awt.event.WindowAdapter() {
                    @Override
                    public void windowClosing(java.awt.event.WindowEvent e) {
                        System.exit(0);
                    }
                });
                dialog.setVisible(true);
            }
        });
    }

public static boolean isLeapYear(int year) {
    assert year >= 1583; // not valid before this date.
    return ((year % 4 == 0) && (year % 100 != 0)) || (year % 400 == 0);
}

/**
 * Prints the sundial
 * @param frame A printable object, or an object that IMPLEMENTS PRINTABLE
 */

public void print(Printable frame) {
    PrinterJob job = PrinterJob.getPrinterJob();
    PageFormat pf = job.defaultPage();
    Paper paper = pf.getPaper();
    paper.setSize(8.5 * 72, 11 * 72);
    pf.setOrientation(PageFormat.LANDSCAPE);
    job.setPrintable(frame, pf);

    boolean ok = job.printDialog();
    if (ok) {
        try {
            job.print();
        } catch (PrinterException ex) {
            /* The job did not successfully complete */
        	JOptionPane.showMessageDialog(null, "Printer is not accepting job.\nSince there is only one page to print," +
        										" make sure you didn't choose to print a page greater than 1.",
        						                "Could not print", JOptionPane.ERROR_MESSAGE);
        }
    }

}

private static String getLocation(String code)
    {
        String address = buildUrl(code);

        String content = null;

        try
        {
            URL url = new URL(address);

            InputStream stream = url.openStream();

            try
            {
                int available = stream.available();

                byte[] bytes = new byte[available];

                stream.read(bytes);

                content = new String(bytes);
            }
            finally
            {
                stream.close();
            }

            return (String) content.toString();
        }
        catch (IOException e)
        {
            throw new RuntimeException(e);
        }
    }

    private static String buildUrl(String code)
    {
        StringBuilder builder = new StringBuilder();

        builder.append(GEO_CODE_SERVER);

        builder.append("address=");
        builder.append(code.replaceAll(" ", "+"));
        builder.append("&sensor=false");

        return builder.toString();
    }

    private static String[] parseLocation(String response)
    {
        String[] lines = response.split("\n");

        String lat = null;
        String lng = null;

        for (int i = 0; i < lines.length; i++)
        {
            if ("\"location\" : {".equals(lines[i].trim()))
            {
                lat = getOrdinate(lines[i+1]);
                lng = getOrdinate(lines[i+2]);
                break;
            }
        }

        return new String[] {lat, lng};
    }

    private static String getOrdinate(String s)
    {
        String[] split = s.trim().split(" ");

        if (split.length < 1)
        {
            return null;
        }

        String ord = split[split.length - 1];

        if (ord.endsWith(","))
        {
            ord = ord.substring(0, ord.length() - 1);
        }

        // Check for valid double
        Double.parseDouble(ord);

        return ord;
    }


    // Variables declaration
    private javax.swing.JButton jButton1;
    private javax.swing.JButton jButton2;
    private javax.swing.JLabel jLabel1;
    private javax.swing.JLabel jLabel2;
    private javax.swing.JLabel jLabel3;
    private javax.swing.JLabel jLabel4;
    private javax.swing.JTextField jTextField1;
    private javax.swing.JTextField jTextField2;
    private javax.swing.JTextField jTextField3;
    private javax.swing.JTextField jTextField4;
    private javax.swing.JTextField jTextField5;
    private JTextField txtEnterAddress;
    private JTextField txtOff;
}
